#include "connpool.h"
#include <iostream>
#include <cstring>
#include <utility>

using namespace std;

/* ---------- 单例 ---------- */
ConnectionPool* ConnectionPool::getConnectionPool()
{
    static ConnectionPool pool;
    return &pool;
}

/* ---------- 配置加载 ---------- */
bool ConnectionPool::loadConfigFile()
{
    FILE* pf = fopen("/home/su/cloudshare/config/mysql.ini", "r");
    if (!pf) { cout << "open mysql.ini failed\n"; return false; }

    char line[1024];
    while (fgets(line, sizeof(line), pf))
    {
        char* sep = strchr(line, '=');
        if (!sep) continue;
        *sep = '\0';
        string key = line;
        string val = sep + 1;
        val.erase(val.find_last_not_of(" \r\n") + 1);

        if (key == "ip")               _ip = val;
        else if (key == "port")        _port = stoi(val);
        else if (key == "username")    _username = val;
        else if (key == "password")    _password = val;
        else if (key == "dbname")      _dbname = val;
        else if (key == "initSize")    _initSize = stoi(val);
        else if (key == "maxSize")     _maxSize = stoi(val);
        else if (key == "maxIdleTime") _maxIdleTime = stoi(val);
        else if (key == "connectionTimeOut") _connectionTimeout = stoi(val);
    }
    fclose(pf);
    return true;
}

/* ---------- 构造 ---------- */
ConnectionPool::ConnectionPool()
{
    if (!loadConfigFile()) { cout << "load config failed\n"; return; }

    /* 预先创建 initSize 个连接，直接放进 write 队列 */
    for (int i = 0; i < _initSize; ++i)
    {
        Connection* p = new Connection();
        if (!p->connect(_ip, _port, _username, _password, _dbname))
        { delete p; continue; }
        p->refreshAliveTime();
        _writeQue.push(p);
        ++_connectionCnt;
    }

    /* 启动生产者线程 */
    thread producer(&ConnectionPool::produceConnectionTask, this);
    producer.detach();

    /* 启动扫描线程 */
    thread scanner(&ConnectionPool::scannerConnectionTask, this);
    scanner.detach();
}

/* ---------- 生产者线程 ---------- */
void ConnectionPool::produceConnectionTask()
{
    for (;;)
    {
        unique_lock<mutex> lk(_mutex);
        /* 等待 read 队列被取空 */
        _cv.wait(lk, [this] { return _readQue.empty(); });

        /* 如果总连接数未达上限，按需新建 */
        while (_connectionCnt < _maxSize && _writeQue.size() < static_cast<size_t>(_maxSize) / 2)
        {
            Connection* p = new Connection();
            if (p->connect(_ip, _port, _username, _password, _dbname))
            { p->refreshAliveTime(); _writeQue.push(p); ++_connectionCnt; }
            else { delete p; }
        }

        /* 交换队列：write → read */
        swap(_readQue, _writeQue);
        _cv.notify_all();
    }
}

/* ---------- 获取连接 ---------- */
shared_ptr<Connection> ConnectionPool::getConnection()
{
    unique_lock<mutex> lk(_mutex);

    /* 等待 read 队列非空 */
    if (_cv.wait_for(lk, chrono::milliseconds(_connectionTimeout),
                     [this] { return !_readQue.empty(); }))
    {
        Connection* raw = _readQue.front();
        _readQue.pop();

        /* 归还到 write 队列 */
        shared_ptr<Connection> sp(raw, [this](Connection* p) {
            p->refreshAliveTime();
            lock_guard<mutex> lk(_mutex);
            _writeQue.push(p);
            _cv.notify_all();   // 可能唤醒生产者
        });
        return sp;
    }
    return nullptr;   // 超时
}

/* ---------- 扫描线程 ---------- */
void ConnectionPool::scannerConnectionTask()
{
    for (;;)
    {
        this_thread::sleep_for(chrono::seconds(_maxIdleTime));

        lock_guard<mutex> lk(_mutex);

        /* 把 read 队列里空闲超时或多余的连接回收 */
        while (!_readQue.empty() && _connectionCnt > _initSize)
        {
            Connection* p = _readQue.front();
            if (p->getAliveeTime() >= _maxIdleTime * 1000)
            {
                _readQue.pop();
                --_connectionCnt;
                delete p;
            }
            else break;
        }

        /* 对 write 队列做同样的事 */
        while (!_writeQue.empty() && _connectionCnt > _initSize)
        {
            Connection* p = _writeQue.front();
            if (p->getAliveeTime() >= _maxIdleTime * 1000)
            {
                _writeQue.pop();
                --_connectionCnt;
                delete p;
            }
            else break;
        }
    }
}
